﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Diagnostics;
using System.Collections;

namespace XiaLastorm.Sharpshooter.Server
{
	/// <summary>
	/// 服务器
	/// </summary>
	public class Server : IDisposable
	{
		/// <summary>
		/// 服务器监听端口
		/// </summary>
		public const int ServerPort = 4660;

		/// <summary>
		/// 获取或设置服务器用于接收新连接的套接字
		/// </summary>
		protected Socket Socket { get; set; }
		/// <summary>
		/// 获取或设置服务器主线程
		/// </summary>
		protected Thread Thread { get; set; }

		/// <summary>
		/// 获取或设置服务器日志
		/// </summary>
		public List<Log> Log { get; set; }
		/// <summary>
		/// 获取或设置客户管理器
		/// </summary>
		public Dictionary<EndPoint, ClientManager> ManagersDictionary { get; protected set; }
		/// <summary>
		/// 获取或设置已登录用户
		/// </summary>
		public Dictionary<User, EndPoint> UsersDictionary { get; protected set; }
		/// <summary>
		/// 用于线程同步的对象
		/// </summary>
		private object lockObject = new object();
		/// <summary>
		/// 是否应该停止工作
		/// </summary>
		private bool shouldStop = false;
		private ManualResetEvent manualEvent;

		/// <summary>
		/// 当用户成功登录时发生。
		/// </summary>
		public event EventHandler<UserEventArgs> UserLoggedIn;
		/// <summary>
		/// 引发用户成功登录事件。
		/// </summary>
		/// <param name="user">用户</param>
		protected void RaiseUserLoggedInEvent(User user)
		{
			if (UserLoggedIn != null)
				UserLoggedIn(this, new UserEventArgs(user));
		}

		/// <summary>
		/// 当用户成功注销时发生。
		/// </summary>
		public event EventHandler<UserEventArgs> UserLoggedOut;
		/// <summary>
		/// 引发用户成功注销事件。
		/// </summary>
		/// <param name="user">用户</param>
		protected void RaiseUserLoggedOutEvent(User user)
		{
			if (UserLoggedOut != null)
				UserLoggedOut(this, new UserEventArgs(user));
		}
		/// <summary>
		/// 当客户端连接到服务器时发生。
		/// </summary>
		public event EventHandler<ClientEventArgs> ClientConnected;
		/// <summary>
		/// 引发客户端连接到服务器事件。
		/// </summary>
		/// <param name="clientManager">相应的客户端管理器</param>
		protected void RaiseClientConnectedEvent(ClientManager clientManager)
		{
			if (ClientConnected != null)
				ClientConnected(this, new ClientEventArgs(clientManager));
		}
		/// <summary>
		/// 当客户端与服务器断开连接时发生。
		/// </summary>
		public event EventHandler<ClientEventArgs> ClientDisconnected;
		/// <summary>
		/// 引发客户端与服务器断开连接事件。
		/// </summary>
		/// <param name="clientManager">相应的客户端管理器</param>
		protected void RaiseClientDisconnectedEvent(ClientManager clientManager)
		{
			if (ClientDisconnected != null)
				ClientDisconnected(this, new ClientEventArgs(clientManager));
		}

		/// <summary>
		/// 当添加了日志时发生。
		/// </summary>
		public event EventHandler<LogAddedEventArgs> LogAdded;
		/// <summary>
		/// 引发添加了日志事件。
		/// </summary>
		/// <param name="addedLog">添加的日志</param>
		protected void RaiseLogAddedEvent(Log addedLog)
		{
			if (LogAdded != null)
				LogAdded(this, new LogAddedEventArgs(addedLog));
		}

		/// <summary>
		/// 服务器状态枚举
		/// </summary>
		public enum ServerState { BeforeOpen = 0, Opened, Closed };

		private ServerState state;
		/// <summary>
		/// 状态
		/// </summary>
		public ServerState State
		{
			get
			{
				lock (lockObject)
				{
					return state;
				}
			}
			protected set
			{
				lock (lockObject)
				{
					if (state == value) return;
					state = value;
				}
				switch (State)
				{
					case ServerState.BeforeOpen:
						break;
					case ServerState.Opened:
						break;
					case ServerState.Closed:
						break;
					default:
						break;
				}
			}
		}

		/// <summary>
		/// 验证服务器还未打开。
		/// </summary>
		protected void ValidateBeforeOpen()
		{
			if (State == ServerState.Opened)
				throw new Exception("服务器处于打开状态");
			if (State == ServerState.Closed)
				throw new Exception("服务器已关闭");
		}

		/// <summary>
		/// 验证服务器已打开。
		/// </summary>
		protected void ValidateOpened()
		{
			if (State == ServerState.BeforeOpen)
				throw new Exception("服务器未打开");
			if (State == ServerState.Closed)
				throw new Exception("服务器已关闭");
		}
		/// <summary>
		/// 验证服务器已关闭。
		/// </summary>
		protected void ValidateClosed()
		{
			if (State == ServerState.BeforeOpen)
				throw new Exception("服务器未打开");
			if (State == ServerState.Opened)
				throw new Exception("服务器处于打开状态");
		}

		/// <summary>
		/// 生成 <see cref="Server"/> class 的新实例。
		/// </summary>
		public Server()
		{
			Log = new List<Sharpshooter.Log>();
			ManagersDictionary = new Dictionary<EndPoint, ClientManager>();
			UsersDictionary = new Dictionary<User, EndPoint>();
		}

		/// <summary>
		/// 打开服务器
		/// </summary>
		public void Open()
		{
			//防止重复使用
			ValidateBeforeOpen();
			State = ServerState.Opened;

			Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			Socket.Bind(new IPEndPoint(IPAddress.Any, ServerPort));
			Socket.Listen(100);

			Thread = new Thread(new ThreadStart(() =>
				{
					while (true)
					{
						//检查是否应该停止
						bool flag = false;
						lock (lockObject)
						{
							if (shouldStop)
							{
								Socket.Close();
								Socket = null;
								Thread = null;
								while (true)								//不能用for循环，因为ClientManager一旦Stop就要从集合中删除
								{
									ClientManager manager = null;
									lock (((ICollection)ManagersDictionary).SyncRoot)
									{
										if (ManagersDictionary.Count > 0)
											manager = ManagersDictionary.Values.First();
										else break;
									}
									manager.Stop();
								}
								flag = true;
							}
						}
						if (flag)
						{
							State = ServerState.Closed;
							if (manualEvent != null)
								manualEvent.Set();
							break;
						}

						//接收新连接
						if (Socket.Poll(100, SelectMode.SelectRead))		//查看是否有新连接
						{
							ClientManager clientManager = new ClientManager(Socket.Accept());
							lock (((ICollection)ManagersDictionary).SyncRoot)
							{
								ManagersDictionary.Add(clientManager.ClientAddress, clientManager);
							}
							AddLog(string.Format("{0} 已连接", clientManager));
							RaiseClientConnectedEvent(clientManager);

							//添加事件处理程序
							clientManager.UserLoggedIn += new EventHandler<UserEventArgs>((sender, e) =>
							{
								lock (((ICollection)UsersDictionary).SyncRoot)
								{
									UsersDictionary.Add(e.User, (sender as ClientManager).ClientAddress);
								}
								lock (((ICollection)(ClientManager.UsersLoggedIn)).SyncRoot)
								{
									ClientManager.UsersLoggedIn.Add(e.User);
								}
								AddLog(string.Format("{0} ({1}) 已登录", e.User, (sender as ClientManager).ClientAddress));
								RaiseUserLoggedInEvent(e.User);
								RefreshFriendsListToAllUsers();
							});
							clientManager.UserLoggedOut += new EventHandler<UserEventArgs>((sender, e) =>
							{
								lock (((ICollection)UsersDictionary).SyncRoot)
								{
									UsersDictionary.Remove(e.User);
								}
								lock (((ICollection)(ClientManager.UsersLoggedIn)).SyncRoot)
								{
									ClientManager.UsersLoggedIn.Remove(e.User);
								}
								AddLog(string.Format("{0} ({1}) 已注销", e.User, (sender as ClientManager).ClientAddress));
								RaiseUserLoggedOutEvent(e.User);
								RefreshFriendsListToAllUsers();
							});
							clientManager.Stoped += new EventHandler((sender, e) =>
							{
								ClientManager manager = sender as ClientManager;
								AddLog(string.Format("{0} 已退出", sender.ToString()));
								if (manager.User != null)
								{
									lock (((ICollection)UsersDictionary).SyncRoot)
									{
										UsersDictionary.Remove(manager.User);
									}
									lock (((ICollection)(ClientManager.UsersLoggedIn)).SyncRoot)
									{
										ClientManager.UsersLoggedIn.Remove(manager.User);
									}
								}
								lock (((ICollection)ManagersDictionary).SyncRoot)
								{
									ManagersDictionary.Remove(manager.ClientAddress);
								}
								RaiseClientDisconnectedEvent(manager);
								RefreshFriendsListToAllUsers();
							});
							clientManager.UserToUserMessageReceived += new EventHandler<UserToUserMessageEventArgs>((sender, e) =>
							{
								ClientManager manager = GetClientManager(e.Message.To);
								if (manager != null)
								{
									AddLog(string.Format("{0}向{1}发送消息", e.Message.From, e.Message.To));
									manager.MessageQueue.Enqueue(e.Message);
								}
								else
								{
									AddLog(string.Format("{0}发送消息给不存在的用户{1}", e.Message.From, e.Message.To));
								}
							});
							clientManager.MessageReceived += new EventHandler<MessageEventArgs>((sender, e) =>
							{
								AddLog(string.Format("收到由{0}发来的{1}消息：{2}", sender, e.Message.GetType().Name, e.Message));
							});
							clientManager.MessageSent += new EventHandler<MessageEventArgs>((sender, e) =>
							{
								AddLog(string.Format("已向{0}发送{1}消息：{2}", sender, e.Message.GetType().Name, e.Message));
							});

							clientManager.Start();
						}
					}
				}));
			Thread.Start();

		}

		/// <summary>
		/// 对所有已登录用户刷新好友列表
		/// </summary>
		protected void RefreshFriendsListToAllUsers()
		{
			List<User> friends = new List<User>();
			lock (((ICollection)UsersDictionary).SyncRoot)
			{
				foreach (var friend in UsersDictionary.Keys)
				{
					friends.Add(new User(friend.ToString()));
				}
			}
			lock (((ICollection)ManagersDictionary).SyncRoot)
			{
				foreach (var manager in ManagersDictionary.Values)
				{
					if (manager.State == ClientManager.ManagerState.Started && manager.User != null)
						manager.SendFriendsList(friends);
				}
			}
		}

		/// <summary>
		/// 关闭服务器，并回收资源
		/// </summary>
		public void Close()
		{
			Dispose();
		}

		/// <summary>
		/// 添加日志
		/// </summary>
		/// <param name="content">日志内容</param>
		protected void AddLog(string content)
		{
			Log log = new Log(content);
			Log.Add(log);
			RaiseLogAddedEvent(log);
		}

		/// <summary>
		/// 执行与释放或重置非托管资源相关的应用程序定义的任务。
		/// </summary>
		public void Dispose()
		{
			lock (lockObject)
			{
				if (shouldStop) return;
				shouldStop = true;
				manualEvent = new ManualResetEvent(false);
			}
			manualEvent.WaitOne();
			ValidateClosed();
			Debug.Assert(Thread == null && Socket == null);
			GC.SuppressFinalize(this);
		}

		/// <summary>
		/// 析构函数
		/// </summary>
		~Server()
		{
			Dispose();
		}

		/// <summary>
		/// 根据用户信息获得相应的客户端管理器
		/// </summary>
		/// <param name="user">The user.</param>
		/// <returns></returns>
		protected ClientManager GetClientManager(User user)
		{
			try
			{
				return ManagersDictionary[UsersDictionary[user]];
			}
			catch
			{
				return null;
			}
		}
	}
}